const fs = require('fs-extra');
const path = require('path');
const { createPluginContainer } = require('../server/pluginContainer.js');
const resolvePlugin = require('../plugins/resolve.js');
const { normalizePath } = require('../utils');
const htmlTypesRE = /\.html$/;
const scriptModuleRE = /<script type="module" src\="(.+?)"><\/script>/;
const JS_TYPES_RE = /\.js$/;

async function esbuildScanPlugin(config, depImports) {
  config.plugins = [resolvePlugin(config)]; // vite | rollup 插件
  const container = await createPluginContainer(config); // 创建插件容器

  const resolve = async (id, importer) => {
    return await container.resolveId(id, importer);
  };
  return {
    name: 'vite:dep-scan',
    setup(build) {
      build.onResolve(
        {
          filter: /\.vue$/,
        },
        async ({ path: id, importer }) => {
          const resolved = await resolve(id, importer); // resolved.id: "D:/project/vite/vite-use/src/App.vue"
          if (resolved) {
            return {
              path: resolved.id,
              external: true,
            };
          }
        }
      );
      // 1. esbuild 的 onResolve 如果匹配到一个并且返回 path 值的话，后面的 onResolve 就不会再匹配了，执行 onLoad 方法
      //    如果 onResolve 返回 external 为 true，则后面的 onResolve、onLoad 方法都不会执行
      // 2. 如果 onLoad 返回内容的 contents 为 'import xxx.js'，则会解析 'import xxx.js', 然后又会递归执行 onResolve 方法，
      //    import { createApp } from 'vue' 处理完的 depImports: { 'vue': 'xxx/node_modules/vue/dist/vue.runtime.esm-bundler.js' }

      // 执行步骤：esbuild.build 的入口文件 index.html -> onResolve html -> onLoad html -> onResolve js -> onLoad js
      //         -  -> onResolve vue 是 node_modules 下的文件，然后 esbuild 插件执行完，返回 depImports 值

      // 用来处理路径的 最开始的 path: "D:\\project\\vite\\vite-use\\index.html"
      build.onResolve({ filter: htmlTypesRE }, async ({ path, importer }) => {
        const resolved = await resolve(path, importer); // { id: "D:/project/vite/vite-use/index.html" }
        if (resolved) {
          return {
            path: resolved.id || resolved,
            namespace: 'html',
          };
        }
      });
      // 对于其它所有的类型文件我们也进行处理
      build.onResolve({ filter: /.*/ }, async ({ path, importer }) => {
        const resolved = await resolve(path, importer);
        if (resolved) {
          const id = resolved.id || resolved; // { id: "D:/project/vite/vite-use/src/main.js" }
          const included = id.includes('node_modules');
          if (included) {
            // main.js 中 import { createApp } from 'vue' 处理完的 depImports: { 'vue': 'xxx/node_modules/vue/dist/vue.runtime.esm-bundler.js' }
            depImports[path] = normalizePath(id);
            return {
              path: id,
              external: true, // 后续的 onResolve、onLoad 方法都不会执行
            };
          }
          return {
            path: id,
          };
        }
        return { path };
      });
      // 用来处理读取内容 自定义读取器
      build.onLoad(
        { filter: htmlTypesRE, namespace: 'html' },
        async ({ path }) => {
          let html = fs.readFileSync(path, 'utf-8'); // 读取 html 文件内容
          const res = html.match(scriptModuleRE); // scriptSrc: '/src/main.js';
          const [, scriptSrc] = res;
          let js = `import ${JSON.stringify(scriptSrc)};\n`; // import '/src/main.js';
          return {
            // 然后又会递归执行 onResolve 方法，执行到第二个 onResolve 方法
            loader: 'js',
            contents: js,
          };
        }
      );
      build.onLoad({ filter: JS_TYPES_RE }, ({ path: id }) => {
        let ext = path.extname(id).slice(1); // 获取文件后缀名
        let contents = fs.readFileSync(id, 'utf-8'); // 读取文件内容
        return {
          loader: ext,
          contents,
        };
      });
    },
  };
}
module.exports = esbuildScanPlugin;
